#!/usr/bin/env python2
# coding: utf8
import gevent.monkey
gevent.monkey.patch_all()
from gevent.pool import Pool
import gevent

from datetime import datetime, timedelta
from itertools import izip
from logging.handlers import TimedRotatingFileHandler
from traceback import format_exc
import logging
import pprint as _pprint
import signal

from osa import Client
from geventhttpclient import HTTPClient, URL
from lxml import objectify
import regex as re


###############################################################################
import config_1111

log = logging.getLogger()

if config_1111.DEBUG:
    log_handler = logging.StreamHandler()
    log_handler.setLevel(logging.DEBUG)
else:
    log_handler = TimedRotatingFileHandler(config_1111.WORKER_LOG_FILE, when='D',
                                           backupCount=7, encoding='utf8')
    log_handler.setLevel(config_1111.LOG_LEVEL)
log_handler.setFormatter(logging.Formatter(config_1111.LOG_FORMAT))
log.addHandler(log_handler)
log.setLevel(config_1111.LOG_LEVEL)

vtms_url = URL(config_1111.VTMS)
vtms_http = HTTPClient.from_url(vtms_url, concurrency=config_1111.VTMS_CONCURRENCY,
                                network_timeout=60.0)
furun_ws = Client(config_1111.FURUN_WS)
GetTradesSoldSearch = furun_ws.service.GetTradesSoldSearch
GetTradeFullInfo = furun_ws.service.GetTradeFullInfo
UpdateOrderInfo = furun_ws.service.UpdateOrderInfo


###############################################################################
class PrettyPrinter(_pprint.PrettyPrinter):
    def format(self, object, context, maxlevels, level):
        if isinstance(object, unicode):
            return (object.encode('utf8'), True, False)
        return _pprint.PrettyPrinter.format(self, object, context, maxlevels,
                                            level)

printer = PrettyPrinter()
pformat = printer.pformat
ONE_MONTH_AGO = timedelta(days=30)
DATETIME_FMT = '%Y-%m-%d %H:%M:%S'
KUNSHAN = re.compile(u'江苏.*?昆山')


def furun(service, *args, **kwargs):
    count = 0
    while True:
        resp = None
        try:
            resp = service(config_1111.APPKEY, config_1111.APPSECRET,
                           timestamp=datetime.now().strftime(DATETIME_FMT),
                           *args, **kwargs)
            xml = objectify.fromstring(resp.encode('utf8'))
            if xml.tag == 'error_response':
                log.error('(%s)%s' % (xml.code.pyval, xml.msg.pyval))
                return
            else:
                return xml
        except:
            # from traceback import print_exc
            # print_exc()
            # import ipdb
            # ipdb.set_trace()
            if resp:
                log.error("%s\n%s" % (format_exc(), resp))
            else:
                log.error(format_exc())
            if count < 5:
                log.error('retry after 3s...')
                gevent.sleep(3)
                count += 1
            else:
                log.error('failed')
                return


class Worker(object):
    stopping = False

    def run(self):
        gevent.spawn(self.pull_orders)

    def shutdown(self):
        log.info('shutting down...')
        self.stopping = True

    def pull_orders(self):
        while True:
            log.info("<- FR")
            poller = gevent.spawn(self.pull)
            try:
                poller.join()
            except:
                log.error(format_exc())
            if self.stopping:
                log.info("stop pulling new orders")
                return
            elif poller.value == 0:
                log.debug("sleeping for 30s...")
                gevent.sleep(30)
        log.debug('quit')

    def pull(self):
        pool = Pool(config_1111.VTMS_CONCURRENCY)
        # pages = None
        page_no = 1
        start = (datetime.now() - ONE_MONTH_AGO).strftime(DATETIME_FMT)
        while True:
            end = datetime.now().strftime(DATETIME_FMT)
            log.debug('%s - %s' % (start, end))
            resp = furun(GetTradesSoldSearch, start, end, str(page_no), '50',
                         'WAIT_CHECK', '-1', wh='011')
            # log.debug('total_results: %s' % resp.total_results)

            size = len(resp.trades.trade)

            if self.stopping:
                log.info("stop processing orders")
                return
            if resp is None:
                log.error('GetTradesSoldSearch failed')
                page_no -= 1
                break
            elif size == 0:
                page_no -= 1
                break
            # elif resp.total_results == 0:
            #     page_no -= 1
            #     break
            else:
                # if not pages:
                #     pages = int(ceil(float(resp.total_results) / 50))
                log.info("page %s" % page_no)
            pool.spawn(self.process, resp.trades.trade)
            if self.stopping:
                log.info("stop processing orders")
                return

            if size < 50:
                break
            else:
            # if page_no < pages:
                page_no += 1
            # else:
            #     break

        if page_no:
            try:
                pool.join()
            except:
                log.error(format_exc())
        return page_no

    def process(self, orders):
        log.info(len(orders))

        info = []
        req = []
        for order in orders:
            try:
                tid = order.tid
            except:
                log.error(order)
                continue
            resp = furun(GetTradeFullInfo, order.tid)
            if resp is None:
                log.error('GetTradeFullInfo failed: %s' % order.tid)
                continue
            try:
                trade = resp.trade
                tid = str(order.tid.pyval)
                province = trade.deliveryProvince.pyval
                city = trade.deliveryCity.pyval
                district = trade.deliveryDistrict.pyval
                address = trade.deliveryAddress.pyval
                order_rem = trade.order_rem.pyval
            except AttributeError:
                continue
            try:
                if province[:2] in {u'香港', u'澳门', u'台湾'}:
                    continue
                addr = u''.join((province, city, district, unicode(address)))
            except:
                log.error(u'skipping: %s %s-%s-%s-%s' %
                          (order.tid, province, city, district, address))
                continue
            req.append({'address': addr})
            info.append((tid, addr, order_rem))

        if not req:
            gevent.sleep(10)
            return 0

        for order in info:
            try:
                self.update_order(order)
            except:
                log.error(format_exc())

    def update_order(self, order):
        (tid, address, order_rem) = order
        try:
            RULE = config_1111.RULES[address[:2]]
        except:
            log.warn('skipping: %s %s' % (tid, address))
            return

        resp = furun(UpdateOrderInfo, tid, warehouse, memo, recommend)
        if resp is None or resp.trade.is_success is False:
            log.error('%s: failed' % tid)

        for (candidate, warehouse) in RULE:
            if candidate in available:
                recommend = config_1111.E_ID_TO_NAME[warehouse][candidate]
                if order_rem:
                    memo = u'%s [VTMS:%s]' % (order_rem, note)
                else:
                    memo = u'[VTMS:%s]' % note
                log.debug(u'%s %s %s %s' % (tid, recommend, warehouse, note))
                return


###############################################################################
def main():
    worker = Worker()
    worker.run()

    for s in [signal.SIGINT, signal.SIGQUIT, signal.SIGTERM]:
        gevent.signal(s, worker.shutdown)

    gevent.wait()


if __name__ == '__main__':
    main()
